package crypto.GM;

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.io.File;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import static utils.security.encodeUtil.base642Byte;
import static utils.security.encodeUtil.byte2Base64;

public class SM2Util {
    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
    private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());

    static {
        if (Security.getProvider("BC") == null) {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        }
    }


    public static String sm2EncryptOld(String data, ECPublicKey key){
        ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(key.getQ(), ecDomainParameters);
        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
        byte[] dataBytes = data.getBytes();
        try {
            byte[] encrypt = sm2Engine.processBlock(dataBytes, 0, dataBytes.length);
            return Hex.toHexString(encrypt);
        } catch (InvalidCipherTextException e) {
            throw new RuntimeException(e);
        }
    }

    public static String sm2DecryptOld(String hexData, ECPrivateKey key){
        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(key.getD(), ecDomainParameters);
        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        sm2Engine.init(false, ecPrivateKeyParameters);
        byte[] data = Hex.decode(hexData);
        try {
            byte[] b = sm2Engine.processBlock(data, 0, data.length);
            return new String(b);
        } catch (InvalidCipherTextException e) {
            throw new RuntimeException(e);
        }
    }


    // -------public Key------------------

    public static KeyPair generateKeyPair(){
        try {
            KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
            kpGen.initialize(ecParameterSpec, new SecureRandom());
            KeyPair kp = kpGen.generateKeyPair();

            System.out.println(Hex.toHexString(kp.getPrivate().getEncoded()));
            System.out.println(Hex.toHexString(kp.getPublic().getEncoded()));

            System.out.println(kp.getPrivate().getAlgorithm()); //EC
            System.out.println(kp.getPrivate().getFormat()); //PKCS#8

            return kp;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    //从文件获取公钥        PublicKey publicKey = getPublickeyFromX509File(new File("/Users/xxx/Downloads/xxxxx.cer"));
    public static PublicKey getPublickeyFromX509File(File file){
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
            FileInputStream in = new FileInputStream(file);
            X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
//            System.out.println(x509.getSerialNumber());
            return x509.getPublicKey();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //SM2的私钥是一个随机数D，公钥是椭圆曲线上的一个点

    //    直接从d生成私钥 ---------------------
    //BigInteger D = new BigInteger("097b5230ef27c7df0fa768289d13ad4e8a96266f0fcb8de40d5942af4293a54a", 16);
    public static BCECPrivateKey getPrivatekeyFromD(String d){
        BigInteger D = new BigInteger(d, 16);
        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(D, ecParameterSpec);
        return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
    }

    //公钥X坐标PublicKeyXHex: 59cf9940ea0809a97b1cbffbb3e9d96d0fe842c1335418280bfc51dd4e08a5d4
    //公钥Y坐标PublicKeyYHex: 9a7f77c578644050e09a9adc4245d1e6eba97554bc8ffd4fe15a78f37f891ff8
    public static BCECPublicKey getPublickeyFromXY(String x, String y){
        BigInteger X = new BigInteger(x, 16);
        BigInteger Y = new BigInteger(y, 16);

        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(X, Y), ecParameterSpec);
        return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
    }


    //----------------从密钥对获取16进制编码  16进制-------------
    public static void getDandXY(KeyPair kp){
        System.out.println("private key d: " + ((BCECPrivateKey)kp.getPrivate()).getD());
        System.out.println("public key q:" + ((BCECPublicKey)kp.getPublic()).getQ()); //{x, y, zs...}
    }

    //公钥，16进制格式
    public static String publicKey2Hex(KeyPair kp){
        ECPoint ecPoint = ((ECPublicKey) kp.getPublic()).getQ();
        return Hex.toHexString(ecPoint.getEncoded(false));
    }

    //私钥，16进制格式
    public static String privateKey2Hex(PrivateKey privateKey){
        BigInteger privatekey = ((ECPrivateKey) privateKey).getD();
        return privatekey.toString(16);
    }


    // --------------------- 下面是不正确的base64编码，因为SM2密钥默认用hex形式的编码 ---------------

    //将Base64编码后的公钥转换成PublicKey对象
    public static ECPublicKey string2PublicKey(String pubStr) throws Exception{
        byte[] keyBytes = base642Byte(pubStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    //将Base64编码后的私钥转换成PrivateKey对象
    public static ECPrivateKey string2PrivateKey(String priStr) throws Exception{
        byte[] keyBytes = base642Byte(priStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        ECPrivateKey privateKey = (ECPrivateKey) keyFactory.generatePrivate(keySpec);
        return privateKey;
    }


    //从密钥对获取(Base64编码)公钥
    public static String getPublicKey(KeyPair keyPair){
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
        byte[] bytes = publicKey.getEncoded();
        return byte2Base64(bytes);
    }

    //从密钥对获取(Base64编码)私钥
    public static String getPrivateKey(KeyPair keyPair){
        ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
        byte[] bytes = privateKey.getEncoded();
        return byte2Base64(bytes);
    }

}
